Completed
Pull Request — master (#34)
by Sander
01:06
created

background.js ➔ ... ➔ API.tabs.then   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 10
rs 9.4285
1
/* global API */
2
3
var background = (function () {
4
    var storage = new API.Storage();
5
    var _self = this;
6
    var _window = {};
7
    API.runtime.onConnect.addListener(function (port) {
8
9
        port.onMessage.addListener(function (msg) {
10
            if (msg === 'credential_amount') {
11
                port.postMessage('credential_amount:' + local_credentials.length);
12
            }
13
14
        });
15
16
    });
17
18
    var master_password = null;
19
20
    function getMasterPasswordSet() {
21
        return (master_password !== null);
22
    }
23
24
    _self.getMasterPasswordSet = getMasterPasswordSet;
25
26
    function setMasterPassword(opts) {
27
        master_password = opts.password;
28
        if (opts.hasOwnProperty('savePassword') && opts.savePassword === true) {
29
            // Save the password in plain text on user request.
30
            // No secure local storage is available :/
31
            storage.set('master_password', opts.password);
32
        } else {
33
            storage.set('master_password', null);
34
        }
35
36
        if (opts.password) {
37
            getSettings();
38
        } else {
39
            displayLogoutIcons();
40
        }
41
42
    }
43
44
    _self.setMasterPassword = setMasterPassword;
45
46
47
    var testMasterPasswordAgainst;
48
49
    function isMasterPasswordValid(password) {
50
        //return true;
51
        try {
52
            PAPI.decryptString(testMasterPasswordAgainst, password);
53
            return true;
54
        } catch (e) {
55
            return false;
56
        }
57
    }
58
59
    _self.isMasterPasswordValid = isMasterPasswordValid;
60
61
62
    var local_credentials = [];
63
    var local_vault = [];
64
    var encryptedFieldSettings = ['default_vault', 'nextcloud_host', 'nextcloud_username', 'nextcloud_password', 'vault_password'];
65
    _self.settings = {};
66
    _self.ticker = null;
67
    _self.running = false;
68
    function getSettings() {
69
70
        storage.get('settings').then(function (_settings) {
71
72
            if (!_settings || !_settings.hasOwnProperty('nextcloud_host')) {
73
                return;
74
            }
75
76
            if (!master_password && _settings.hasOwnProperty('nextcloud_username') && _settings.hasOwnProperty('vault_password')) {
77
                _self.settings.isInstalled = 1;
78
                testMasterPasswordAgainst = _settings.nextcloud_username;
79
                return;
80
            }
81
82
            for (var i = 0; i < encryptedFieldSettings.length; i++) {
83
                var field = encryptedFieldSettings[i];
84
                _settings[field] = JSON.parse(PAPI.decryptString(_settings[field], master_password));
85
            }
86
87
88
            _self.settings = _settings;
89
90
91
            PAPI.host = _settings.nextcloud_host;
92
            PAPI.username = _settings.nextcloud_username;
93
            PAPI.password = _settings.nextcloud_password;
94
            if (!_settings.vault_password) {
95
                return;
96
            }
97
            if (PAPI.credentialsSet()) {
98
                getCredentials();
99
                if (_self.running) {
100
                    clearInterval(_self.ticker);
101
                }
102
103
                _self.running = true;
104
                _self.ticker = setInterval(function () {
105
                    getCredentials();
106
                }, _self.settings.refreshTime * 1000);
107
            } else {
108
                console.log('Login details are missing!');
0 ignored issues
show
Debugging Code introduced by
console.log looks like debug code. Are you sure you do not want to remove it?
Loading history...
109
            }
110
        });
111
    }
112
113
    _self.getSettings = getSettings;
114
115
    function getRuntimeSettings() {
116
        return _self.settings;
117
    }
118
119
    _self.getRuntimeSettings = getRuntimeSettings;
120
121
    function getSetting(name) {
122
        return _self.settings[name];
123
    }
124
    _self.getSetting = getSetting;
125
126
    function saveSettings(settings, cb) {
0 ignored issues
show
Unused Code introduced by
The parameter cb is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
127
        for (var i = 0; i < encryptedFieldSettings.length; i++) {
128
            var field = encryptedFieldSettings[i];
129
            settings[field] = PAPI.encryptString(JSON.stringify(settings[field]), master_password);
130
        }
131
        PAPI.host = settings.nextcloud_host;
132
        PAPI.username = settings.nextcloud_username;
133
        PAPI.password = settings.nextcloud_password;
134
        //window.settings contains the run-time settings
135
        _self.settings = settings;
136
137
138
        storage.set('settings', settings).then(function () {
139
            getSettings();
140
        });
141
142
    }
143
144
    _self.saveSettings = saveSettings;
145
146
147
    function getCredentials() {
148
        //console.log('Loading vault with the following settings: ', settings);
149
        var tmpList = [];
150
        PAPI.getVault(_self.settings.default_vault.guid, function (vault) {
151
            if (vault.hasOwnProperty('error')) {
152
                return;
153
            }
154
            var _credentials = vault.credentials;
155
            for (var i = 0; i < _credentials.length; i++) {
156
                var key = _self.settings.vault_password;
157
                var credential = _credentials[i];
158
                if (credential.hidden === 1) {
159
                    continue;
160
                }
161
                var usedKey = key;
162
                //Shared credentials are not implemented yet
163
                if (credential.hasOwnProperty('shared_key') && credential.shared_key) {
164
                    usedKey = PAPI.decryptString(credential.shared_key, key);
165
166
                }
167
                credential = PAPI.decryptCredential(credential, usedKey);
168
                if (credential.delete_time === 0) {
169
                    tmpList.push(credential);
170
                }
171
172
            }
173
            delete vault.credentials;
174
            local_vault = vault;
175
            local_credentials = tmpList;
176
            updateTabsIcon();
177
        });
178
    }
179
180
    _self.getCredentials = getCredentials;
181
182
    function getCredentialsByUrl(_url, sender) {
0 ignored issues
show
Unused Code introduced by
The parameter sender is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
183
        if (!master_password) {
184
            return [];
185
        }
186
        if (!_url || _url === '') {
187
            return [];
188
        }
189
        var url = processURL(_url, _self.settings.ignoreProtocol, _self.settings.ignoreSubdomain, _self.settings.ignorePath, _self.settings.ignorePort);
190
        var found_list = [];
191
        for (var i = 0; i < local_credentials.length; i++) {
192
            if (local_credentials[i].url && local_credentials[i].username && local_credentials[i].password) {
193
                if (local_credentials[i].url.indexOf(url) !== -1) {
194
                    found_list.push(local_credentials[i]);
195
                }
196
            }
197
        }
198
        return found_list;
199
    }
200
201
    _self.getCredentialsByUrl = getCredentialsByUrl;
202
203
204
    function getCredentialForHTTPAuth(req) {
205
        return getCredentialsByUrl(req.url)[0];
206
    }
207
208
    _window.getCredentialForHTTPAuth = getCredentialForHTTPAuth;
209
210
    var mined_data = [];
211
212
    function minedForm(data, sender) {
213
        var url = sender.url;
214
        var existingLogins = getCredentialsByUrl(sender.url);
215
        var title = "Detected new login:";
216
        var minedMatchingID = null;
217
        for (var j = 0; j < existingLogins.length; j++) {
218
            var login = existingLogins[j];
219
            if (login.username === data.username) {
220
                if (login.password !== data.password) {
221
                    minedMatchingID = login.guid;
222
                    title = "Detected changed password for user:";
223
                }
224
                else {
225
                    //console.log('No changes detected');
226
                    delete mined_data[sender.tab.id];
227
                    return;
228
                }
229
            }
230
        }
231
        mined_data[sender.tab.id] = {
232
            title: title,
233
            url: url,
234
            username: data.username,
235
            password: data.password,
236
            label: sender.title,
237
            guid: minedMatchingID
238
        };
239
240
        //console.log('Done mining, ', mined_data, sender.tab.id);
241
    }
242
243
    _self.minedForm = minedForm;
244
245
    function getMinedData(args, sender) {
246
        //console.log('Fecthing  mined data for tab id', sender.tab.id)
247
        return mined_data[sender.tab.id];
248
    }
249
250
    _self.getMinedData = getMinedData;
251
252
    function clearMined(args, sender) {
253
        delete mined_data[sender.tab.id];
254
    }
255
256
    _self.clearMined = clearMined;
257
258
    function saveMinedCallback(args) {
259
        createIconForTab(args.sender.tab);
260
        API.tabs.query({active: true, currentWindow: true}).then(function (tabs) {
261
            API.tabs.sendMessage(args.sender.tab.id, {method: "minedLoginSaved", args: args}).then(function (response) {
0 ignored issues
show
Unused Code introduced by
The parameter response is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
262
            });
263
        });
264
    }
265
266
    function ignoreSite(_url) {
267
        if(!_self.settings.hasOwnProperty('ignored_sites')){
268
            _self.settings.ignored_sites = []
269
        }
270
        var site = processURL(_url, _self.settings.ignoreProtocol, _self.settings.ignoreSubdomain, _self.settings.ignorePath, _self.settings.ignorePort);
271
        if(_self.settings.ignored_sites.indexOf(site) === -1){
272
            _self.settings.ignored_sites.push(site);
273
            saveSettings(_self.settings);
274
        }
275
    }
276
277
    _self.ignoreSite = ignoreSite;
278
279
    function passToParent(args, sender) {
280
        API.tabs.sendMessage(sender.tab.id, {method: args.injectMethod, args: args.args}).then(function (response) {
0 ignored issues
show
Unused Code introduced by
The parameter response is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
281
        });
282
    }
283
284
    _self.passToParent = passToParent;
285
286
    function getActiveTab(opt) {
287
        API.tabs.query({active: true, currentWindow: true}).then(function (tabs) {
288
            var tab = tabs[0];
289
            API.tabs.sendMessage(tab.id, {method: opt.returnFn, args: tab}).then(function (response) {
0 ignored issues
show
Unused Code introduced by
The parameter response is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
290
            });
291
        });
292
    }
293
294
    _self.getActiveTab = getActiveTab;
295
296
    function updateCredentialUrlDoorhanger(login) {
297
        API.tabs.query({active: true, currentWindow: true}).then(function (tabs) {
298
            var tab = tabs[0];
299
            var data = login;
300
            data.url = tab.url;
301
            data.title = 'Detected changed url for: ';
302
            API.tabs.sendMessage(tab.id, {
303
                method: 'showUrlUpdateDoorhanger',
304
                args: {data: data}
305
            });
306
        });
307
    }
308
309
    _self.updateCredentialUrlDoorhanger = updateCredentialUrlDoorhanger;
310
311
    function updateCredentialUrl(data, sender) {
312
        mined_data[sender.tab.id] = data;
313
        saveMined({}, sender);
314
315
    }
316
    _self.updateCredentialUrl = updateCredentialUrl;
317
318
    function saveMined(args, sender) {
319
        var data = mined_data[sender.tab.id];
320
        var credential,
321
            credential_index;
322
323
        if (data.guid === null) {
324
            credential = PAPI.newCredential();
325
        } else {
326
            for (var i = 0; i < local_credentials.length; i++) {
327
                if (local_credentials[i].guid === data.guid) {
328
                    credential = local_credentials[i];
329
                    credential_index = i;
330
                    break;
331
                }
332
            }
333
        }
334
        credential.username = data.username;
0 ignored issues
show
Bug introduced by
The variable credential seems to not be initialized for all possible execution paths.
Loading history...
335
        credential.password = data.password;
336
        credential.url = sender.tab.url;
337
        if (credential.guid !== null) {
338
            PAPI.updateCredential(credential, _self.settings.vault_password, function (updatedCredential) {
339
                if(credential_index){
340
                    local_credentials[credential_index] = updatedCredential;
341
                }
342
                saveMinedCallback({credential: credential, updated: true, sender: sender});
0 ignored issues
show
Bug introduced by
The variable credential seems to not be initialized for all possible execution paths.
Loading history...
343
                delete mined_data[sender.tab.id];
344
            });
345
        } else {
346
            credential.label = sender.tab.title;
347
            credential.vault_id = local_vault.vault_id;
348
            PAPI.createCredential(credential, _self.settings.vault_password, function (createdCredential) {
349
                saveMinedCallback({credential: credential, updated: false, sender: sender});
0 ignored issues
show
Bug introduced by
The variable credential seems to not be initialized for all possible execution paths.
Loading history...
350
                local_credentials.push(createdCredential);
351
                delete mined_data[sender.tab.id];
352
            });
353
        }
354
    }
355
356
    _self.saveMined = saveMined;
357
358
    function searchCredential(searchText) {
359
        var searchFields = ['label', 'username', 'email', 'url', 'description'];
360
        var results = [];
361
        for (var i = 0; i < local_credentials.length; i++) {
362
            var credential = local_credentials[i];
363
            for (var f = 0; f < searchFields.length; f++) {
364
                var field = searchFields[f];
365
                if (credential[field] && credential[field].indexOf(searchText) !== -1) {
366
                    results.push(credential);
367
                    break;
368
                }
369
            }
370
        }
371
        return results;
372
    }
373
374
    _self.searchCredential = searchCredential;
375
376
377
    function injectCreateCredential(args, sender) {
378
        var credential = PAPI.newCredential();
379
        credential.label = args.label;
380
        credential.username = args.username;
381
        credential.password = args.username;
382
        credential.vault_id = local_vault.vault_id;
383
        credential.url = sender.tab.url;
384
        PAPI.createCredential(credential, _self.settings.vault_password, function (createdCredential) {
385
            saveMinedCallback({credential: credential, updated: false, sender: sender, selfAdded: true});
386
            local_credentials.push(createdCredential);
387
388
        });
389
    }
390
391
    self.injectCreateCredential = injectCreateCredential;
0 ignored issues
show
Bug introduced by
The variable self seems to be never declared. If this is a global, consider adding a /** global: self */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
392
393
    function isVaultKeySet() {
394
        return (_self.settings.vault_password !== null);
395
    }
396
397
    _self.isVaultKeySet = isVaultKeySet;
398
399
    function isAutoFillEnabled() {
400
        if (!_self.settings.hasOwnProperty('disableAutoFill')) {
401
            return true;
402
        }
403
        return (_self.settings.disableAutoFill === false);
404
    }
405
406
    _self.isAutoFillEnabled = isAutoFillEnabled;
407
408
    API.runtime.onMessage.addListener(function (msg, sender, sendResponse) {
409
410
        if (!msg || !msg.hasOwnProperty('method')) {
411
            return;
412
        }
413
        var result = false;
414
        if (_self[msg.method]) {
415
            result = _self[msg.method](msg.args, sender);
416
        } else {
417
            console.warn('[NOT FOUND] Method call', msg.method, 'args: ', msg.args);
418
        }
419
420
        sendResponse(result);
421
    });
422
423
    var defaultColor = '#0082c9';
424
425
    function createIconForTab(tab) {
426
        if (!master_password) {
427
            return;
428
        }
429
        var tabUrl = tab.url;
430
        var logins = getCredentialsByUrl(tabUrl);
431
        if (tab.active) {
432
            window.contextMenu.setContextItems(logins);
433
        }
434
        var credentialAmount = logins.length;
435
        API.browserAction.setBadgeText({
436
            text: credentialAmount.toString(),
437
            tabId: tab.id
438
        });
439
        API.browserAction.setBadgeBackgroundColor({
440
            color: defaultColor,
441
            tabId: tab.id
442
        });
443
        var plural = (credentialAmount === 1) ? 'credential' : 'credentials';
444
        API.browserAction.setTitle({
445
            title: 'Passman - ' + credentialAmount.toString() + ' ' + plural + ' found for this page',
446
            tabId: tab.id
447
        });
448
    }
449
450
    function displayLogoutIcons() {
451
        if (_self.settings) {
452
            API.tabs.query({}).then(function (tabs) {
453
                for (var t = 0; t < tabs.length; t++) {
454
                    var tab = tabs[t];
455
                    API.browserAction.setBadgeText({
456
                        text: '🔑',
457
                        tabId: tab.id
458
                    });
459
                    API.browserAction.setBadgeBackgroundColor({
460
                        color: '#ff0000',
461
                        tabId: tab.id
462
                    });
463
                    API.browserAction.setTitle({
464
                        title: 'Passman - Locked',
465
                        tabId: tab.id
466
                    });
467
                }
468
            });
469
        }
470
    }
471
472
    function updateTabsIcon() {
473
        API.tabs.query({}).then(function (tabs) {
474
            for (var t = 0; t < tabs.length; t++) {
475
                var tab = tabs[t];
476
                createIconForTab(tab);
477
            }
478
        });
479
    }
480
481
482
    API.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) {
483
        if (master_password) {
484
            createIconForTab(tab);
485
        } else {
486
            displayLogoutIcons();
487
        }
488
    });
489
490
    API.tabs.onActivated.addListener(function () {
491
        API.tabs.query({active: true, currentWindow: true}).then(function (tabs) {
492
            if (master_password) {
493
                createIconForTab(tabs[0]);
494
            } else {
495
                displayLogoutIcons();
496
            }
497
        });
498
    });
499
500
    displayLogoutIcons();
501
502
    storage.get('master_password').then(function (password) {
503
        if (password) {
504
            master_password = password;
505
            API.api.browserAction.setBadgeBackgroundColor({
506
                color: defaultColor
507
            });
508
        }
509
        getSettings();
510
    }).error(function (error) {
511
        if (error === "Data not found") {
512
            getSettings();
513
        }
514
    });
515
    return _window;
516
}());
517
518